【数据结构】B树的创建

B树也是一种搜索树,二叉搜索树、红黑树、都是动态查找树,典型的二叉搜索结构,查找的时间复杂度和树的高度相关O(log2N)。

这些二叉搜索结构有一个共同的缺陷:数据量大,树的高度太高,增大访问磁盘的次数,从而效率低下。

想要加速对数据的访问速度:
1.提高I/O的时间
2.降低树的高度——平衡多叉树

B树的定义
一棵M阶(M>2)的B树,是一棵平衡的M路平衡搜索树,可以是空树或者满足以下性质:
1.根节点至少有两个孩子。
2.每个非根节点至少有M/2(上取整)个孩子,至多有M个孩子。
3.每个非根节点至少有M/2-1(上取整)个关键字,至多有M-1个关键字,并且以升序排列
4.key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间。
5.所有的叶子节点都在同一层。

例:
插入{53,75,139,49,145,36,101};

M阶B树——M=3
3阶B树的插入过程:
这里写图片描述
这里写图片描述这里写图片描述这里写图片描述
代码实现过程:
1.首先设定结构体,实际只用到两个关键码,但是为了交换简单,我们设置三个关键码,孩子指针域始终比关键码多一个。
这里写图片描述
2.创建一个类将B树的函数操作封装起来。
3.B树的插入:
(1)根节点为空,开辟一个新节点newNode,将key的值赋给newNode,对size+1,最后使根指向newNode。
(2)根不为空,通过Find函数找到插入位置cur,通过_InsertKey()函数将key的值赋给cur,(cur->size)+1,判断cur->size与M的大小,如果cur->size < M则return true;
(3)如果cur->size=M,要将节点进行分裂,将中间的关键码向上提,new一个节点将最右边的关键码赋值给key[0]。
(4)每次改变节点要记得更改双亲的指向。

【BTree.h】

#pragma once
#include<iostream>
using namespace std;
#define M 3

template<class K>
struct BTreeNode
{
    K _keys[M];        //关键码
    BTreeNode<K>* _pSub[M + 1];  //孩子指针域
    BTreeNode<K>* _pParent;
    size_t size;
    BTreeNode()
        :_pParent(NULL)
        , size(0)
    {
        for (int i = 0; i < (M + 1); i++)
            _pSub[i] = NULL;
    }
};

template<class K>
class BTree
{
    typedef BTreeNode<K> Node;
    typedef Node* pNode;
public:
    BTree()
     :pRoot(NULL)
    {}
    bool BTree_Insert(K value)
    {
            if (pRoot == NULL)
            {
                pRoot = new Node;
                pRoot->_keys[0] = value;
                pRoot->size = 1;
                return true;
            }

            //节点的关键字满了就进行分裂
            pair<pNode,int> findNode = _Find(value);    
            if (findNode.second >= 0)     //找到相同关键码
                return false;

            //没找到相同节点,可以插入新节点
            pNode cur = findNode.first;
            K newkey = value; 
            pNode sub = NULL;

            //向cur插入newkey,sub
            while (1)
            {
                _InsertKey(cur, newkey, sub);   //插入一个孩子和一个关键字
                if (cur->size < M)
                    return true;
                else
                {
                    //需要分裂
                    pNode newNode = _splitblock(cur);
                    K midkey = cur->_keys[(cur->size) / 2];
                    //根节点分裂
                    cur->size = (cur->size) - (newNode->size + 1);
                    if (cur == pRoot)
                    {
                        pRoot = new Node;
                        pRoot->_keys[0] = midkey;
                        pRoot->size = 1;
                        pRoot->_pSub[0] = cur;
                        pRoot->_pSub[1] = newNode;
                        cur->_pParent = pRoot;
                        newNode->_pParent = pRoot;

                        return true;
                    }
                    else
                    {
                        sub = newNode;
                        newkey = midkey;
                        cur = cur->_pParent;
                    }
                }
            }
    }
    void InOrder()        //中序遍历
    {
        _InOrder(pRoot);
        cout << endl;
    }
private:
    pNode _splitblock(pNode cur)    //分裂函数
    {  
        pNode newNode = new Node;
        int mid = (cur->size) / 2;
        size_t j = 0;
        size_t i = mid + 1;

        for (; i < cur->size; i++)
        {
            newNode->_keys[j] = cur->_keys[i];
            newNode->_pSub[j] = cur->_pSub[i];
            if (newNode->_pSub[j] != NULL)
                newNode->_pSub[j]->_pParent = newNode;
            newNode->size++;
            j++;
        }


        //拷右孩子
        newNode->_pSub[j] = cur->_pSub[i];
        if (cur->_pSub[i] != NULL)
            newNode->_pSub[j]->_pParent = newNode;

        return newNode;
    }

    void _InsertKey(pNode cur,const K value,pNode sub)         //插入一个孩子和一个关键码
    {
        int end = cur->size - 1;
        while (end >= 0)
        {
            if (cur->_keys[end] > value)
            {
                cur->_keys[end + 1] = cur->_keys[end];
                cur->_pSub[end + 2] = cur->_pSub[end + 1];
                end--;
            }
            else
                break;
        }

        //当end<0 或 value > cur->keys[end]
        cur->_keys[end + 1] = value;
        cur->_pSub[end + 2] = sub;

        if (sub != NULL)
            sub->_pParent = cur;
        cur->size++;
    }

    pair<pNode,int> _Find(K value)
    {
        pNode ret = pRoot;
        pNode parent = NULL;
        while (ret)
        {
            size_t i = 0;
            while (i < ret->size)
            {
                //在当前位置的左树
                if (value < (ret->_keys[i]))
                    break;

                else if (value>(ret->_keys[i]))
                {
                    i++;
                }
                else                         //有相同关键码
                    return make_pair(ret, i);     
            }
            //在左树或没找到
            parent = ret;
            ret = ret->_pSub[i];
        }
        return make_pair(parent,-1);
    }

    void _InOrder(pNode _pRoot)     //中序遍历
    {
        if (_pRoot == NULL)
            return;

        pNode cur = _pRoot;
        size_t i = 0;
        for (i = 0; i < cur->size; i++)
        {
            _InOrder(cur->_pSub[i]);
            cout << cur->_keys[i] << " ";
        }
        _InOrder(cur->_pSub[i]);
    }

private:
    pNode pRoot;
};

void test()
{
    int arr[] = { 53, 75, 139, 49, 145, 36, 101 };
    int size = sizeof(arr) / sizeof(arr[0]);
    BTree<int> bt;
    for (int i = 0; i < size; i++)
    {
        bt.BTree_Insert(arr[i]);
    }
    bt.InOrder();
}

【test.cpp】

#include"BTree.h"
int main()
{
    test();
    system("pause");
    return 0;
}
  • 15
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
该资源包含了几乎所有的数据结构的动画视频,帮助我们更好的理解数据结构与算法的编程思路。 目录如下: 'B树的删除.swf', 'B树的生长过程.swf', '三元组表的转置.swf', '中序线索化二叉树.swf', '串的顺序存储.swf', '二分查找.swf', '二叉排序树的删除.swf', '二叉排序树的生成.swf', '二叉树的建立.swf', '克鲁斯卡尔算法构造最小生成树.swf', '冒泡排序.swf', '分块查找.swf', '单链表结点的删除.swf', '单链表结点的插入.swf', '图的深度优先遍历.swf', '基数排序.swf', '堆排序.swf', '头插法建单链表.swf', '寻找中序线索化二叉树指定结点的前驱.swf', '寻找中序线索化二叉树指定结点的后继.swf', '尾插法建表.swf', '希儿排序.swf', '开放定址法建立散列表.swf', '归并排序.swf', '循环队列操作演示.swf', '快速排序.swf', '拉链法创建散列表.swf', '拓扑排序.swf', '最短路径.swf', '朴素串匹配算法过程示意.swf', '构造哈夫曼树的算法模拟.swf', '构造哈夫曼树过程.swf', '栈与递归.swf', '树、森林和二叉树的转换.swf', '桶式排序法.swf', '直接插入排序.swf', '直接选择排序.swf', '邻接表表示的图的广度优先遍历.swf', '邻接表表示的图的深度优先遍历.swf', '顺序查找.swf', '顺序栈(4个存储空间).swf', '顺序栈(8个存储空间).swf', '顺序表的删除运算.swf', '顺序表的插入.swf', '顺序队列操作.swf'。 (注:.swf动画格式可直接使用播放器打开。)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值